﻿using CSRedis;
using Dapper;
using Himp.TaskScheduling.Hangfire.Jobs;
using Himp.TaskScheduling.Hangfire.Models;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using System.Data;
using System.Text;

namespace Himp.TaskScheduling.Hangfire.Workers
{
    public class AlarmWorker : IHostedService, IDisposable
    {
        #region SQL

        public const string EXIST_EVENTMSGREC_SQL = @"
            SELECT CASE WHEN 
            EXISTS (SELECT 1 FROM [TaskScheduling].[TS_EventMsgRec] WHERE TopicFullPath = @TopicFullPath
            AND FORMAT(CreationTime, 'yyyyMMddHHmm') =@CreationTime) 
            THEN 1 
            ELSE 0 
            END";

        public const string INSERT_EVENTMSGREC_RECORD = @"
            INSERT INTO
            [TaskScheduling].[TS_EventMsgRec]
            (
            Id,
            TopicFullPath,
            MsgTopicName,
            MsgCont,
            EventMsgOperStas,
            CreationTime,
            ExtraProperties,
            ConcurrencyStamp
            )
            VALUES
            (
            @Id,
            @TopicFullPath,
            @MsgTopicName,
            @MsgCont,
            @EventMsgOperStas,
            @CreationTime,
            @ExtraProperties,
            @ConcurrencyStamp
            )
            ";

        #endregion

        #region 字段

        private CSRedisClient _client;
        private Timer _timer;
        private readonly TimeSpan _interval = TimeSpan.FromMilliseconds(1000);
        private readonly TimeSpan _delay = TimeSpan.FromMilliseconds(0);
        private bool _isExecuting = false;
        Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
        private static readonly object _lock = new object();
        private bool _init = false;
        private ILogger<RedisToMqttWorker> _logger;
        private readonly ConnectionStringOption _connectionStrings;

        #endregion

        #region 方法

        public AlarmWorker(ILogger<RedisToMqttWorker> logger
            , IOptions<ConnectionStringOption> connectionStrings)
        {
            _connectionStrings = connectionStrings.Value;
            _logger = logger;
            // 初始化计时器，初始时不启动，等待 StartAsync 启动
            _timer = new Timer(ExecuteJob, null, _delay, Timeout.InfiniteTimeSpan);
            _client = new CSRedis.CSRedisClient(_connectionStrings.KaiYRedis);
            RedisHelper.Initialization(_client);
            _init = true;
        }

        private async void ExecuteJob(object state)
        {
            if (_isExecuting || !_init)
            {
                // 如果任务正在执行，则跳过
                return;
            }

            lock (_lock)
            {
                if (_isExecuting)
                {
                    return;
                }
                _isExecuting = true;
            }

            try
            {
                // 你的任务逻辑
                await RunAsync();

            }
            catch (Exception e)
            {
                await Console.Out.WriteLineAsync(e.Message);
            }
            finally
            {
                GC.Collect(0);
                lock (_lock)
                {
                    _isExecuting = false;
                }
                // 任务完成后重新设置计时器
                _timer.Change(_interval, Timeout.InfiniteTimeSpan);
            }
        }

        private async Task RunAsync()
        {
            var keys = RedisHelper.Keys("*");

            if (MqttClientHelper.Connected)
            {
                var tasks = keys.Where(k => (k.StartsWith("APS1:PLC1:AL") && (k.EndsWith(":Value") || k.EndsWith(":Stamp"))) ||
                    (k.StartsWith("ASS1:PLC1:AL") && (k.EndsWith(":Value") || k.EndsWith(":Stamp"))) ||
                    (k.StartsWith("ASS2:PLC1:AL") && (k.EndsWith(":Value") || k.EndsWith(":Stamp"))) ||
                    (k.StartsWith("SPT1:PLC1") && (k.EndsWith("报警:Value") || k.EndsWith("报警:Stamp"))))
                    .Select(async key =>
                {
                    await Console.Out.WriteLineAsync(key);
                    var v = await RedisHelper.GetAsync(key);
                    var keyPath = key.Split(":");

                    if (keyPath.Length > 3 
                    && ((keyPath[3].EndsWith("Value") && !string.IsNullOrEmpty(v) && v != "0")
                        || (keyPath[3].EndsWith("Stamp"))))
                    {
                        var topicFullPath = keyPath[0] + "/" + keyPath[1];
                        DateTime createDate = DateTime.Now;

                        if (keyPath[3] == "Stamp")
                        {
                            DateTime.TryParse(v, out createDate);
                        }

                        try
                        {
                            using (IDbConnection connection = new SqlConnection(_connectionStrings.RL_Web))
                            {
                                connection.Open();
                                // 检查 ID 是否已存在
                                bool exists = connection.ExecuteScalar<bool>(EXIST_EVENTMSGREC_SQL,
                                    new { TopicFullPath = topicFullPath, CreationTime = createDate.ToString("yyyyMMddHHmm") });
                                EventMsgRec eventMsgRec = new EventMsgRec(topicFullPath, "报警记录", keyPath[2], createDate);

                                if (!exists)
                                {
                                    // 读取数据
                                    var insertResult = connection.Execute(INSERT_EVENTMSGREC_RECORD, eventMsgRec);
                                    await MqttClientHelper.PublishStringAsync(topicFullPath + "/Alarm", $"{keyPath[2]}");
                                }
                            }
                        }
                        catch (Exception e)
                        {

                        }
                    }
                });

                await Task.WhenAll(tasks);
            }
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _timer.Change(_delay, _interval);
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Dispose();
            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }

        #endregion
    }
}
